home *** CD-ROM | disk | FTP | other *** search
/ C/C++ Users Group Library 1996 July / C-C++ Users Group Library July 1996.iso / vol_200 / 216_01 / rz.c < prev    next >
Encoding:
C/C++ Source or Header  |  1980-01-01  |  24.6 KB  |  1,183 lines

  1. #define VERSION "1.13 01-01-87"
  2. #define PUBDIR "/usr/spool/uucppublic"
  3.  
  4. /*% cc  -DNFGVMIN -DCRCTABLE -K -O -i % -o rz; size rz
  5.  *
  6.  * rz.c By Chuck Forsberg
  7.  *
  8.  *    cc -O rz.c -o rz        USG (3.0) Unix
  9.  *     cc -O -DV7  rz.c -o rz        Unix V7, BSD 2.8 - 4.3
  10.  *
  11.  *    ln rz rb            For either system
  12.  *
  13.  *    ln rz /usr/bin/rzrmail        For remote mail.  Make this the
  14.  *                    login shell. rzrmail then calls
  15.  *                    rmail(1) to deliver mail.
  16.  *
  17.  *        define CRCTABLE to use table driven CRC
  18.  *
  19.  *  Unix is a trademark of Western Electric Company
  20.  *
  21.  * A program for Unix to receive files and commands from computers running
  22.  *  Professional-YAM, PowerCom, YAM, IMP, or programs supporting XMODEM.
  23.  *  rz uses Unix buffered input to reduce wasted CPU time.
  24.  *
  25.  * Iff the program is invoked by rzCOMMAND, output is piped to 
  26.  * "COMMAND filename"
  27.  *
  28.  *  Some systems (Venix, Coherent, Regulus) may not support tty raw mode
  29.  *  read(2) the same way as Unix. ONEREAD must be defined to force one
  30.  *  character reads for these systems. Added 7-01-84 CAF
  31.  *
  32.  *  Alarm signal handling changed to work with 4.2 BSD 7-15-84 CAF 
  33.  *
  34.  *  NFGVMIN Added 1-13-85 CAF for PC-AT Xenix systems where c_cc[VMIN]
  35.  *  doesn't seem to work (even though it compiles without error!).
  36.  *
  37.  *  HOWMANY should be tuned for best performance
  38.  *
  39.  *  USG UNIX (3.0) ioctl conventions courtesy  Jeff Martin
  40.  */
  41. #define LOGFILE "/tmp/rzlog"
  42. #define zperr vfile
  43.  
  44. #include <stdio.h>
  45. #include <signal.h>
  46. #include <setjmp.h>
  47. #include <ctype.h>
  48. FILE *popen();
  49.  
  50. #define OK 0
  51. #define FALSE 0
  52. #define TRUE 1
  53. #define ERROR (-1)
  54.  
  55. /*
  56.  * Max value for HOWMANY is 255.
  57.  *   A larger value reduces system overhead but may evoke kernel bugs.
  58.  *   133 corresponds to a XMODEM/CRC sector
  59.  */
  60. #ifndef HOWMANY
  61. #define HOWMANY 133
  62. #endif
  63.  
  64. int Zmodem=0;        /* ZMODEM protocol requested */
  65. int Nozmodem = 0;    /* If invoked as "rb" */
  66. unsigned Baudrate;
  67. #include "rbsb.c"    /* most of the system dependent stuff here */
  68.  
  69. char *substr();
  70. FILE *fout;
  71.  
  72. /*
  73.  * Routine to calculate the free bytes on the current file system
  74.  *  ~0 means many free bytes (unknown)
  75.  */
  76. long getfree()
  77. {
  78.     return(~0L);    /* many free bytes ... */
  79. }
  80.  
  81. /* Ward Christensen / CP/M parameters - Don't change these! */
  82. #define ENQ 005
  83. #define CAN ('X'&037)
  84. #define XOFF ('s'&037)
  85. #define XON ('q'&037)
  86. #define SOH 1
  87. #define STX 2
  88. #define EOT 4
  89. #define ACK 6
  90. #define NAK 025
  91. #define CPMEOF 032
  92. #define WANTCRC 0103    /* send C not NAK to get crc not checksum */
  93. #define TIMEOUT (-2)
  94. #define RCDO (-3)
  95. #define ERRORMAX 5
  96. #define RETRYMAX 5
  97. #define WCEOT (-10)
  98. #define SECSIZ 128    /* cp/m's Magic Number record size */
  99. #define PATHLEN 257    /* ready for 4.2 bsd ? */
  100. #define KSIZE 1024    /* record size with k option */
  101. #define UNIXFILE 0x8000    /* happens to the the S_IFREG file mask bit for stat */
  102.  
  103. int Lastrx;
  104. int Crcflg;
  105. int Firstsec;
  106. int Eofseen;        /* indicates cpm eof (^Z) has been received */
  107. int errors;
  108. int Restricted=0;    /* restricted; no /.. or ../ in filenames */
  109. #ifdef ONEREAD
  110. /* Sorry, Regulus and some others don't work right in raw mode! */
  111. int Readnum = 1;    /* Number of bytes to ask for in read() from modem */
  112. #else
  113. int Readnum = HOWMANY;    /* Number of bytes to ask for in read() from modem */
  114. #endif
  115.  
  116. #define DEFBYTL 2000000000L    /* default rx file size */
  117. long Bytesleft;        /* number of bytes of incoming file left */
  118. long Modtime;        /* Unix style mod time for incoming file */
  119. short Filemode;        /* Unix style mode for incoming file */
  120. char Pathname[PATHLEN];
  121. char *Progname;        /* the name by which we were called */
  122.  
  123. int Batch=0;
  124. int Wcsmask=0377;
  125. int Topipe=0;
  126. int MakeLCPathname=TRUE;    /* make received pathname lower case */
  127. int Verbose=0;
  128. int Quiet=0;        /* overrides logic that would otherwise set verbose */
  129. int Nflag = 0;        /* Don't really transfer files */
  130. int Rxbinary=FALSE;    /* receive all files in bin mode */
  131. int Rxascii=FALSE;    /* receive files in ascii (translate) mode */
  132. int Thisbinary;        /* current file is to be received in bin mode */
  133. int Blklen;        /* record length of received packets */
  134. char secbuf[KSIZE];
  135. char linbuf[HOWMANY];
  136. int Lleft=0;        /* number of characters in linbuf */
  137. time_t timep[2];
  138. char Lzmanag;        /* Local file management request */
  139. char zconv;        /* ZMODEM file conversion request */
  140. char zmanag;        /* ZMODEM file management request */
  141. char ztrans;        /* ZMODEM file transport request */
  142.  
  143. jmp_buf tohere;        /* For the interrupt on RX timeout */
  144.  
  145. #include "zm.c"
  146.  
  147. int tryzhdrtype=ZRINIT;    /* Header type to send corresponding to Last rx close */
  148.  
  149. alrm()
  150. {
  151.     longjmp(tohere, -1);
  152. }
  153.  
  154. /* called by signal interrupt or terminate to clean things up */
  155. bibi(n)
  156. {
  157.     if (Zmodem)
  158.         zmputs(Attn);
  159.     canit(); mode(0);
  160.     fprintf(stderr, "rz: caught signal %d; exiting", n);
  161.     exit(128+n);
  162. }
  163.  
  164. main(argc, argv)
  165. char *argv[];
  166. {
  167.     register char *cp;
  168.     register npats;
  169.     char *virgin, **patts;
  170.     char *getenv();
  171.     int exitcode;
  172.  
  173.     Rxtimeout = 100;
  174.     setbuf(stderr, NULL);
  175.     if ((cp=getenv("SHELL")) && (substr(cp, "rsh") || substr(cp, "rksh")))
  176.         Restricted=TRUE;
  177.  
  178.     chkinvok(virgin=argv[0]);    /* if called as [-]rzCOMMAND set flag */
  179.     npats = 0;
  180.     while (--argc) {
  181.         cp = *++argv;
  182.         if (*cp == '-') {
  183.             while( *++cp) {
  184.                 switch(*cp) {
  185.                 case '+':
  186.                     Lzmanag = ZMAPND; break;
  187.                 case '1':
  188.                     iofd = 1; break;
  189.                 case '7':
  190.                     Wcsmask = 0177;
  191.                 case 'a':
  192.                     Rxascii=TRUE;  break;
  193.                 case 'b':
  194.                     Rxbinary=TRUE; break;
  195.                 case 'c':
  196.                     Crcflg=TRUE; break;
  197.                 case 'D':
  198.                     Nflag = TRUE; break;
  199.                 case 'p':
  200.                     Lzmanag = ZMPROT;  break;
  201.                 case 'q':
  202.                     Quiet=TRUE; Verbose=0; break;
  203.                 case 't':
  204.                     if (--argc < 1) {
  205.                         usage();
  206.                     }
  207.                     Rxtimeout = atoi(*++argv);
  208.                     if (Rxtimeout<10 || Rxtimeout>1000)
  209.                         usage();
  210.                     break;
  211.                 case 'u':
  212.                     MakeLCPathname=FALSE; break;
  213.                 case 'v':
  214.                     ++Verbose; break;
  215.                 default:
  216.                     usage();
  217.                 }
  218.             }
  219.         }
  220.         else if ( !npats && argc>0) {
  221.             if (argv[0][0]) {
  222.                 npats=argc;
  223.                 patts=argv;
  224.             }
  225.         }
  226.     }
  227.     if (npats > 1)
  228.         usage();
  229.     if (Verbose) {
  230.         if (freopen(LOGFILE, "a", stderr)==NULL) {
  231.             printf("Can't open log file %s\n",LOGFILE);
  232.             exit(0200);
  233.         }
  234.         setbuf(stderr, NULL);
  235.         fprintf(stderr, "argv[0]=%s Progname=%s\n", virgin, Progname);
  236.     }
  237.     if (fromcu() && !Quiet) {
  238.         if (Verbose == 0)
  239.             Verbose = 2;
  240.     }
  241.     mode(1);
  242.     if (signal(SIGINT, bibi) == SIG_IGN) {
  243.         signal(SIGINT, SIG_IGN); signal(SIGKILL, SIG_IGN);
  244.     }
  245.     else {
  246.         signal(SIGINT, bibi); signal(SIGKILL, bibi);
  247.     }
  248.     if (wcreceive(npats, patts)==ERROR) {
  249.         exitcode=0200;
  250.         canit();
  251.     }
  252.     mode(0);
  253.     if (exitcode && !Zmodem)    /* bellow again with all thy might. */
  254.         canit();
  255.     exit(exitcode);
  256. }
  257.  
  258.  
  259. usage()
  260. {
  261.     fprintf(stderr,"%s %s for %s by Chuck Forsberg\n",
  262.       Progname, VERSION, OS);
  263.     fprintf(stderr,"Usage:    rz [-1abuv]        (ZMODEM Batch)\n");
  264.     fprintf(stderr,"or    rb [-1abuv]        (YMODEM Batch)\n");
  265.     fprintf(stderr,"or    rz [-1abcv] file    (XMODEM or XMODEM-1k)\n");
  266.     fprintf(stderr,"      -1 For cu(1): Use fd 1 for input\n");
  267.     fprintf(stderr,"      -a ASCII transfer (strip CR)\n");
  268.     fprintf(stderr,"      -b Binary transfer for all files\n");
  269.     fprintf(stderr,"      -v Verbose more v's give more info\n");
  270.     fprintf(stderr,"      -c Use 16 bit CRC    (XMODEM)\n");
  271.     exit(1);
  272. }
  273. /*
  274.  *  Debugging information output interface routine
  275.  */
  276. /* VARARGS1 */
  277. vfile(f, a, b, c)
  278. register char *f;
  279. {
  280.     if (Verbose > 1) {
  281.         fprintf(stderr, f, a, b, c);
  282.         fprintf(stderr, "\n");
  283.     }
  284. }
  285.  
  286. /*
  287.  * Let's receive something already.
  288.  */
  289.  
  290. char *rbmsg =
  291. "%s ready. To begin transfer, type \"%s file ...\" to your modem program\r\n";
  292.  
  293. wcreceive(argc, argp)
  294. char **argp;
  295. {
  296.     register c;
  297.  
  298.     if (Batch || argc==0) {
  299.         Crcflg=(Wcsmask==0377);
  300.         if ( !Quiet)
  301.             fprintf(stderr, rbmsg, Progname, Nozmodem?"sb":"sz");
  302.         if (c=tryz()) {
  303.             if (c == ZCOMPL)
  304.                 return OK;
  305.             if (c == ERROR)
  306.                 goto fubar;
  307.             c = rzfiles();
  308.             if (c)
  309.                 goto fubar;
  310.         } else {
  311.             for (;;) {
  312.                 if (wcrxpn(secbuf)== ERROR)
  313.                     goto fubar;
  314.                 if (secbuf[0]==0)
  315.                     return OK;
  316.                 if (procheader(secbuf) == ERROR)
  317.                     goto fubar;
  318.                 if (wcrx()==ERROR)
  319.                     goto fubar;
  320.             }
  321.         }
  322.     } else {
  323.         Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  324.  
  325.         strcpy(Pathname, *argp);
  326.         checkpath(Pathname);
  327.         fprintf(stderr, "\nrz: ready to receive %s\r\n", Pathname);
  328.         if ((fout=fopen(Pathname, "w")) == NULL)
  329.             return ERROR;
  330.         if (wcrx()==ERROR)
  331.             goto fubar;
  332.     }
  333.     return OK;
  334. fubar:
  335.     canit();
  336.     if (Topipe && fout) {
  337.         pclose(fout);  return ERROR;
  338.     }
  339.     if (fout)
  340.         fclose(fout);
  341.     if (Restricted) {
  342.         unlink(Pathname);
  343.         fprintf(stderr, "\r\nrz: %s removed.\r\n", Pathname);
  344.     }
  345.     return ERROR;
  346. }
  347.  
  348.  
  349. /*
  350.  * Fetch a pathname from the other end as a C ctyle ASCIZ string.
  351.  * Length is indeterminate as long as less than Blklen
  352.  * A null string represents no more files (YMODEM)
  353.  */
  354. wcrxpn(rpn)
  355. char *rpn;    /* receive a pathname */
  356. {
  357.     register c;
  358.  
  359. #ifdef NFGVMIN
  360.     readline(1);
  361. #else
  362.     purgeline();
  363. #endif
  364.  
  365. et_tu:
  366.     Firstsec=TRUE;  Eofseen=FALSE;
  367.     sendline(Crcflg?WANTCRC:NAK);
  368.     Lleft=0;    /* Do read next time ... */
  369.     while ((c = wcgetsec(rpn, 100)) != 0) {
  370.         log( "Pathname fetch returned %d\n", c);
  371.         if (c == WCEOT) {
  372.             sendline(ACK);
  373.             Lleft=0;    /* Do read next time ... */
  374.             readline(1);
  375.             goto et_tu;
  376.         }
  377.         return ERROR;
  378.     }
  379.     sendline(ACK);
  380.     return OK;
  381. }
  382.  
  383. /*
  384.  * Adapted from CMODEM13.C, written by
  385.  * Jack M. Wierda and Roderick W. Hart
  386.  */
  387.  
  388. wcrx()
  389. {
  390.     register int sectnum, sectcurr;
  391.     register char sendchar;
  392.     register char *p;
  393.     int cblklen;            /* bytes to dump this block */
  394.  
  395.     Firstsec=TRUE;sectnum=0; Eofseen=FALSE;
  396.     sendchar=Crcflg?WANTCRC:NAK;
  397.  
  398.     for (;;) {
  399.         sendline(sendchar);    /* send it now, we're ready! */
  400.         Lleft=0;    /* Do read next time ... */
  401.         sectcurr=wcgetsec(secbuf, (sectnum&0177)?50:130);
  402.         report(sectcurr);
  403.         if (sectcurr==(sectnum+1 &Wcsmask)) {
  404.             sectnum++;
  405.             cblklen = Bytesleft>Blklen ? Blklen:Bytesleft;
  406.             if (putsec(secbuf, cblklen)==ERROR)
  407.                 return ERROR;
  408.             if ((Bytesleft-=cblklen) < 0)
  409.                 Bytesleft = 0;
  410.             sendchar=ACK;
  411.         }
  412.         else if (sectcurr==(sectnum&Wcsmask)) {
  413.             log( "Received dup Sector\n");
  414.             sendchar=ACK;
  415.         }
  416.         else if (sectcurr==WCEOT) {
  417.             if (closeit())
  418.                 return ERROR;
  419.             sendline(ACK);
  420.             Lleft=0;    /* Do read next time ... */
  421.             return OK;
  422.         }
  423.         else if (sectcurr==ERROR)
  424.             return ERROR;
  425.         else {
  426.             log( "Sync Error\n");
  427.             return ERROR;
  428.         }
  429.     }
  430. }
  431.  
  432. /*
  433.  * Wcgetsec fetches a Ward Christensen type sector.
  434.  * Returns sector number encountered or ERROR if valid sector not received,
  435.  * or CAN CAN received
  436.  * or WCEOT if eot sector
  437.  * time is timeout for first char, set to 4 seconds thereafter
  438.  ***************** NO ACK IS SENT IF SECTOR IS RECEIVED OK **************
  439.  *    (Caller must do that when he is good and ready to get next sector)
  440.  */
  441.  
  442. wcgetsec(rxbuf, maxtime)
  443. char *rxbuf;
  444. int maxtime;
  445. {
  446.     register checksum, wcj, firstch;
  447.     register unsigned short oldcrc;
  448.     register char *p;
  449.     int sectcurr;
  450.  
  451.     for (Lastrx=errors=0; errors<RETRYMAX; errors++) {
  452.  
  453.         if ((firstch=readline(maxtime))==STX) {
  454.             Blklen=KSIZE; goto get2;
  455.         }
  456.         if (firstch==SOH) {
  457.             Blklen=SECSIZ;
  458. get2:
  459.             sectcurr=readline(1);
  460.             if ((sectcurr+(oldcrc=readline(1)))==Wcsmask) {
  461.                 oldcrc=checksum=0;
  462.                 for (p=rxbuf,wcj=Blklen; --wcj>=0; ) {
  463.                     if ((firstch=readline(1)) < 0)
  464.                         goto bilge;
  465.                     oldcrc=updcrc(firstch, oldcrc);
  466.                     checksum += (*p++ = firstch);
  467.                 }
  468.                 if ((firstch=readline(1)) < 0)
  469.                     goto bilge;
  470.                 if (Crcflg) {
  471.                     oldcrc=updcrc(firstch, oldcrc);
  472.                     if ((firstch=readline(1)) < 0)
  473.                         goto bilge;
  474.                     oldcrc=updcrc(firstch, oldcrc);
  475.                     if (oldcrc & 0xFFFF)
  476.                         log("CRC=0%o\n", oldcrc);
  477.                     else {
  478.                         Firstsec=FALSE;
  479.                         return sectcurr;
  480.                     }
  481.                 }
  482.                 else if (((checksum-firstch)&Wcsmask)==0) {
  483.                     Firstsec=FALSE;
  484.                     return sectcurr;
  485.                 }
  486.                 else
  487.                     log( "Checksum Error\n");
  488.             }
  489.             else
  490.                 log("Sector number garbled 0%o 0%o\n",
  491.                  sectcurr, oldcrc);
  492.         }
  493.         /* make sure eot really is eot and not just mixmash */
  494. #ifdef NFGVMIN
  495.         else if (firstch==EOT && readline(1)==TIMEOUT)
  496.             return WCEOT;
  497. #else
  498.         else if (firstch==EOT && Lleft==0)
  499.             return WCEOT;
  500. #endif
  501.         else if (firstch==CAN) {
  502.             if (Lastrx==CAN) {
  503.                 log( "Sender CANcelled\n");
  504.                 return ERROR;
  505.             } else {
  506.                 Lastrx=CAN;
  507.                 continue;
  508.             }
  509.         }
  510.         else if (firstch==TIMEOUT) {
  511.             if (Firstsec)
  512.                 goto humbug;
  513. bilge:
  514.             log( "Timeout\n");
  515.         }
  516.         else
  517.             log( "Got 0%o sector header\n", firstch);
  518.  
  519. humbug:
  520.         Lastrx=0;
  521.         while(readline(1)!=TIMEOUT)
  522.             ;
  523.         if (Firstsec) {
  524.             sendline(Crcflg?WANTCRC:NAK);
  525.             Lleft=0;    /* Do read next time ... */
  526.         } else {
  527.             maxtime=40; sendline(NAK);
  528.             Lleft=0;    /* Do read next time ... */
  529.         }
  530.     }
  531.     /* try to stop the bubble machine. */
  532.     canit();
  533.     return ERROR;
  534. }
  535.  
  536. /*
  537.  * This version of readline is reasoably well suited for
  538.  * reading many characters.
  539.  *  (except, currently, for the Regulus version!)
  540.  *
  541.  * timeout is in tenths of seconds
  542.  */
  543. readline(timeout)
  544. int timeout;
  545. {
  546.     register n;
  547.     static char *cdq;    /* pointer for removing chars from linbuf */
  548.  
  549.     if (--Lleft >= 0) {
  550.         if (Verbose > 8) {
  551.             fprintf(stderr, "%02x ", *cdq&0377);
  552.         }
  553.         return (*cdq++ & Wcsmask);
  554.     }
  555.     n = timeout/10;
  556.     if (n < 2)
  557.         n = 3;
  558.     if (Verbose > 3)
  559.         fprintf(stderr, "Calling read: n=%d ", n);
  560.     if (setjmp(tohere)) {
  561. #ifdef TIOCFLUSH
  562. /*        ioctl(iofd, TIOCFLUSH, 0); */
  563. #endif
  564.         Lleft = 0;
  565.         if (Verbose>1)
  566.             fprintf(stderr, "Readline:TIMEOUT\n");
  567.         return TIMEOUT;
  568.     }
  569.     signal(SIGALRM, alrm); alarm(n);
  570.     Lleft=read(iofd, cdq=linbuf, Readnum);
  571.     alarm(0);
  572.     if (Verbose > 3) {
  573.         fprintf(stderr, "Read returned %d bytes\n", Lleft);
  574.     }
  575.     if (Lleft < 1)
  576.         return TIMEOUT;
  577.     --Lleft;
  578.     if (Verbose > 8) {
  579.         fprintf(stderr, "%02x ", *cdq&0377);
  580.     }
  581.     return (*cdq++ & Wcsmask);
  582. }
  583.  
  584.  
  585.  
  586. /*
  587.  * Purge the modem input queue of all characters
  588.  */
  589. purgeline()
  590. {
  591.     Lleft = 0;
  592. #ifdef USG
  593.     ioctl(iofd, TCFLSH, 0);
  594. #else
  595.     lseek(iofd, 0L, 2);
  596. #endif
  597. }
  598.  
  599.  
  600. /*
  601.  * Process incoming file information header
  602.  */
  603. procheader(name)
  604. char *name;
  605. {
  606.     register char *openmode, *p, **pp;
  607.  
  608.     /* set default parameters and overrides */
  609.     openmode = "w";
  610.     Thisbinary = Rxbinary || !Rxascii;
  611.     if (Lzmanag)
  612.         zmanag = Lzmanag;
  613.  
  614.     /*
  615.      *  Process ZMODEM remote file management requests
  616.      */
  617.     if (!Rxbinary && zconv == ZCNL)    /* Remote ASCII override */
  618.         Thisbinary = 0;
  619.     if (zconv == ZCBIN)    /* Remote Binary override */
  620.         ++Thisbinary;
  621.     else if (zmanag == ZMAPND)
  622.         openmode = "a";
  623.     /* ZMPROT check for existing file */
  624.     if (zmanag == ZMPROT && (fout=fopen(name, "r"))) {
  625.         fclose(fout);  return ERROR;
  626.     }
  627.  
  628.     Bytesleft = DEFBYTL; Filemode = 0; Modtime = 0L;
  629.  
  630.     p = name + 1 + strlen(name);
  631.     if (*p) {    /* file coming from Unix or DOS system */
  632.         sscanf(p, "%ld%lo%o", &Bytesleft, &Modtime, &Filemode);
  633.         if (Filemode & UNIXFILE)
  634.             ++Thisbinary;
  635.         if (Verbose) {
  636.             fprintf(stderr,  "Incoming: %s %ld %lo %o\n",
  637.               name, Bytesleft, Modtime, Filemode);
  638.         }
  639.     }
  640.     else {        /* File coming from CP/M system */
  641.         for (p=name; *p; ++p)        /* change / to _ */
  642.             if ( *p == '/')
  643.                 *p = '_';
  644.  
  645.         if ( *--p == '.')        /* zap trailing period */
  646.             *p = 0;
  647.     }
  648.  
  649.     if (!Zmodem && MakeLCPathname && !IsAnyLower(name))
  650.         uncaps(name);
  651.     if (Topipe) {
  652.         sprintf(Pathname, "%s %s", Progname+2, name);
  653.         if (Verbose)
  654.             fprintf(stderr,  "Topipe: %s %s\n",
  655.               Pathname, Thisbinary?"BIN":"ASCII");
  656.         if ((fout=popen(Pathname, "w")) == NULL)
  657.             return ERROR;
  658.     } else {
  659.         strcpy(Pathname, name);
  660.         if (Verbose) {
  661.             fprintf(stderr,  "Receiving %s %s %s\n",
  662.               name, Thisbinary?"BIN":"ASCII", openmode);
  663.         }
  664.         checkpath(name);
  665.         if (Nflag)
  666.             name = "/dev/null";
  667.         if ((fout=fopen(name, openmode)) == NULL)
  668.             return ERROR;
  669.     }
  670.     return OK;
  671. }
  672.  
  673. /*
  674.  * Putsec writes the n characters of buf to receive file fout.
  675.  *  If not in binary mode, carriage returns, and all characters
  676.  *  starting with CPMEOF are discarded.
  677.  */
  678. putsec(buf, n)
  679. char *buf;
  680. register n;
  681. {
  682.     register char *p;
  683.  
  684.     if (Thisbinary) {
  685.         for (p=buf; --n>=0; )
  686.             putc( *p++, fout);
  687.     }
  688.     else {
  689.         if (Eofseen)
  690.             return OK;
  691.         for (p=buf; --n>=0; ++p ) {
  692.             if ( *p == '\r')
  693.                 continue;
  694.             if (*p == CPMEOF) {
  695.                 Eofseen=TRUE; return OK;
  696.             }
  697.             putc(*p ,fout);
  698.         }
  699.     }
  700.     return OK;
  701. }
  702.  
  703. /*
  704.  *  Send a character to modem.  Small is beautiful.
  705.  */
  706. sendline(c)
  707. {
  708.     char d;
  709.  
  710.     d = c;
  711.     if (Verbose>4)
  712.         fprintf(stderr, "Sendline: %x\n", c);
  713.     write(1, &d, 1);
  714. }
  715.  
  716. xsendline(c)
  717. {
  718.     sendline(c);
  719. }
  720.  
  721. flushmo() {}
  722.  
  723.  
  724.  
  725.  
  726. /* make string s lower case */
  727. uncaps(s)
  728. register char *s;
  729. {
  730.     for ( ; *s; ++s)
  731.         if (isupper(*s))
  732.             *s = tolower(*s);
  733. }
  734. /*
  735.  * IsAnyLower returns TRUE if string s has lower case letters.
  736.  */
  737. IsAnyLower(s)
  738. register char *s;
  739. {
  740.     for ( ; *s; ++s)
  741.         if (islower(*s))
  742.             return TRUE;
  743.     return FALSE;
  744. }
  745.  
  746. /*
  747.  * substr(string, token) searches for token in string s
  748.  * returns pointer to token within string if found, NULL otherwise
  749.  */
  750. char *
  751. substr(s, t)
  752. register char *s,*t;
  753. {
  754.     register char *ss,*tt;
  755.     /* search for first char of token */
  756.     for (ss=s; *s; s++)
  757.         if (*s == *t)
  758.             /* compare token with substring */
  759.             for (ss=s,tt=t; ;) {
  760.                 if (*tt == 0)
  761.                     return s;
  762.                 if (*ss++ != *tt++)
  763.                     break;
  764.             }
  765.     return NULL;
  766. }
  767.  
  768. /*
  769.  * Log an error
  770.  */
  771. /*VARARGS1*/
  772. log(s,p,u)
  773. char *s, *p, *u;
  774. {
  775.     if (!Verbose)
  776.         return;
  777.     fprintf(stderr, "error %d: ", errors);
  778.     fprintf(stderr, s, p, u);
  779. }
  780.  
  781. /* send cancel string to get the other end to shut up */
  782. canit()
  783. {
  784.     static char canistr[] = {
  785.      24,24,24,24,24,24,24,24,24,24,8,8,8,8,8,8,8,8,8,8,0
  786.     };
  787.  
  788.     printf(canistr);
  789.     Lleft=0;    /* Do read next time ... */
  790.     fflush(stdout);
  791. }
  792.  
  793.  
  794. /*
  795.  * Return 1 iff stdout and stderr are different devices
  796.  *  indicating this program operating with a modem on a
  797.  *  different line
  798.  */
  799. fromcu()
  800. {
  801.     struct stat a, b;
  802.     fstat(1, &a); fstat(2, &b);
  803.     return (a.st_rdev != b.st_rdev);
  804. }
  805.  
  806. report(sct)
  807. int sct;
  808. {
  809.     if (Verbose>1)
  810.         fprintf(stderr,"%03d%c",sct,sct%10? ' ' : '\r');
  811. }
  812.  
  813. /*
  814.  * If called as [-][dir/../]vrzCOMMAND set Verbose to 1
  815.  * If called as [-][dir/../]rzCOMMAND set the pipe flag
  816.  * If called as rb use YMODEM protocol
  817.  */
  818. chkinvok(s)
  819. char *s;
  820. {
  821.     register char *p;
  822.  
  823.     p = s;
  824.     while (*p == '-')
  825.         s = ++p;
  826.     while (*p)
  827.         if (*p++ == '/')
  828.             s = p;
  829.     if (*s == 'v') {
  830.         Verbose=1; ++s;
  831.     }
  832.     Progname = s;
  833.     if (s[0]=='r' && s[1]=='b')
  834.         Nozmodem = TRUE;
  835.     if (s[2] && s[0]=='r' && s[1]=='b')
  836.         Topipe=TRUE;
  837.     if (s[2] && s[0]=='r' && s[1]=='z')
  838.         Topipe=TRUE;
  839. }
  840.  
  841. /*
  842.  * Totalitarian Communist pathname processing
  843.  */
  844. checkpath(name)
  845. char *name;
  846. {
  847.     if (Restricted) {
  848.         if (fopen(name, "r") != NULL) {
  849.             canit();
  850.             fprintf(stderr, "\r\nrz: %s exists\n", name);
  851.             bibi();
  852.         }
  853.         /* restrict pathnames to current tree or uucppublic */
  854.         if ( substr(name, "../")
  855.          || (name[0]== '/' && strncmp(name, PUBDIR, strlen(PUBDIR))) ) {
  856.             canit();
  857.             fprintf(stderr,"\r\nrz:\tSecurity Violation\r\n");
  858.             bibi();
  859.         }
  860.     }
  861. }
  862.  
  863. /*
  864.  * Initialize for Zmodem receive attempt, try to activate Zmodem sender
  865.  *  Handles ZSINIT frame
  866.  *  Return ZFILE if Zmodem filename received, -1 on error,
  867.  *   ZCOMPL if transaction finished,  else 0
  868.  */
  869. tryz()
  870. {
  871.     register c, n;
  872.     register cmdzack1flg;
  873.  
  874.     if (Nozmodem)        /* Check for "rb" program name */
  875.         return 0;
  876.  
  877.  
  878.     for (n=Zmodem?10:5; --n>=0; ) {
  879.         /* Set buffer length (0) and capability flags */
  880.         stohdr(0L);
  881. #ifdef CANBREAK
  882.         Txhdr[ZF0] = CANFDX|CANOVIO|CANBRK;
  883. #else
  884.         Txhdr[ZF0] = CANFDX|CANOVIO;
  885. #endif
  886.         zshhdr(tryzhdrtype, Txhdr);
  887. again:
  888.         switch (zgethdr(Rxhdr, 0)) {
  889.         case ZRQINIT:
  890.             continue;
  891.         case ZEOF:
  892.             continue;
  893.         case TIMEOUT:
  894.             continue;
  895.         case ZFILE:
  896.             zconv = Rxhdr[ZF0];
  897.             zmanag = Rxhdr[ZF1];
  898.             ztrans = Rxhdr[ZF2];
  899.             tryzhdrtype = ZRINIT;
  900.             if (zrdata(secbuf, KSIZE) == GOTCRCW)
  901.                 return ZFILE;
  902.             zshhdr(ZNAK, Txhdr);
  903.             goto again;
  904.         case ZSINIT:
  905.             if (zrdata(Attn, ZATTNLEN) == GOTCRCW) {
  906.                 zshhdr(ZACK, Txhdr);
  907.                 goto again;
  908.             }
  909.             zshhdr(ZNAK, Txhdr);
  910.             goto again;
  911.         case ZFREECNT:
  912.             stohdr(getfree());
  913.             zshhdr(ZACK, Txhdr);
  914.             goto again;
  915.         case ZCOMMAND:
  916.             cmdzack1flg = Rxhdr[ZF0];
  917.             if (zrdata(secbuf, KSIZE) == GOTCRCW) {
  918.                 if (cmdzack1flg & ZCACK1)
  919.                     stohdr(0L);
  920.                 else
  921.                     stohdr((long)sys2(secbuf));
  922.                 purgeline();    /* dump impatient questions */
  923.                 do {
  924.                     zshhdr(ZCOMPL, Txhdr);
  925.                 }
  926.                 while (++errors<10 && zgethdr(Rxhdr,1) != ZFIN);
  927.                 ackbibi();
  928.                 if (cmdzack1flg & ZCACK1)
  929.                     exec2(secbuf);
  930.                 return ZCOMPL;
  931.             }
  932.             zshhdr(ZNAK, Txhdr); goto again;
  933.         case ZCOMPL:
  934.             goto again;
  935.         default:
  936.             continue;
  937.         case ZFIN:
  938.             ackbibi(); return ZCOMPL;
  939.         case ZCAN:
  940.             return ERROR;
  941.         }
  942.     }
  943.     return 0;
  944. }
  945.  
  946. /*
  947.  * Receive 1 or more files with ZMODEM protocol
  948.  */
  949. rzfiles()
  950. {
  951.     register c;
  952.  
  953.     for (;;) {
  954.         switch (c = rzfile()) {
  955.         case ZEOF:
  956.         case ZSKIP:
  957.             switch (tryz()) {
  958.             case ZCOMPL:
  959.                 return OK;
  960.             default:
  961.                 return ERROR;
  962.             case ZFILE:
  963.                 break;
  964.             }
  965.             continue;
  966.         default:
  967.             return c;
  968.         case ERROR:
  969.             return ERROR;
  970.         }
  971.     }
  972. }
  973.  
  974. /*
  975.  * Receive a file with ZMODEM protocol
  976.  *  Assumes file name frame is in secbuf
  977.  */
  978. rzfile()
  979. {
  980.     register c, n;
  981.     long rxbytes;
  982.  
  983.     Eofseen=FALSE;
  984.     if (procheader(secbuf) == ERROR) {
  985.         return (tryzhdrtype = ZSKIP);
  986.     }
  987.  
  988.     n = 10; rxbytes = 0l;
  989.  
  990.     for (;;) {
  991.         stohdr(rxbytes);
  992.         zshhdr(ZRPOS, Txhdr);
  993. nxthdr:
  994.         switch (c = zgethdr(Rxhdr, 0)) {
  995.         default:
  996.             vfile("rzfile: zgethdr returned %d", c);
  997.             return ERROR;
  998.         case ZNAK:
  999.         case TIMEOUT:
  1000.             if ( --n < 0) {
  1001.                 vfile("rzfile: zgethdr returned %d", c);
  1002.                 return ERROR;
  1003.             }
  1004.         case ZFILE:
  1005.             zrdata(secbuf, KSIZE);
  1006.             continue;
  1007.         case ZEOF:
  1008.             if (rclhdr(Rxhdr) != rxbytes) {
  1009.                 continue;
  1010.             }
  1011.             if (closeit()) {
  1012.                 tryzhdrtype = ZFERR;
  1013.                 vfile("rzfile: closeit returned <> 0");
  1014.                 return ERROR;
  1015.             }
  1016.             vfile("rzfile: normal EOF");
  1017.             return c;
  1018.         case ERROR:    /* Too much garbage in header search error */
  1019.             if ( --n < 0) {
  1020.                 vfile("rzfile: zgethdr returned %d", c);
  1021.                 return ERROR;
  1022.             }
  1023.             zmputs(Attn);
  1024.             continue;
  1025.         case ZDATA:
  1026.             if (rclhdr(Rxhdr) != rxbytes) {
  1027.                 if ( --n < 0) {
  1028.                     return ERROR;
  1029.                 }
  1030.                 zmputs(Attn);  continue;
  1031.             }
  1032. moredata:
  1033.             switch (c = zrdata(secbuf, KSIZE)) {
  1034.             case ZCAN:
  1035.                 vfile("rzfile: zgethdr returned %d", c);
  1036.                 return ERROR;
  1037.             case ERROR:    /* CRC error */
  1038.                 if ( --n < 0) {
  1039.                     vfile("rzfile: zgethdr returned %d", c);
  1040.                     return ERROR;
  1041.                 }
  1042.                 zmputs(Attn);
  1043.                 continue;
  1044.             case TIMEOUT:
  1045.                 if ( --n < 0) {
  1046.                     vfile("rzfile: zgethdr returned %d", c);
  1047.                     return ERROR;
  1048.                 }
  1049.                 continue;
  1050.             case GOTCRCW:
  1051.                 n = 10;
  1052.                 putsec(secbuf, Rxcount);
  1053.                 rxbytes += Rxcount;
  1054.                 stohdr(rxbytes);
  1055.                 zshhdr(ZACK, Txhdr);
  1056.                 goto nxthdr;
  1057.             case GOTCRCQ:
  1058.                 n = 10;
  1059.                 putsec(secbuf, Rxcount);
  1060.                 rxbytes += Rxcount;
  1061.                 stohdr(rxbytes);
  1062.                 zshhdr(ZACK, Txhdr);
  1063.                 goto moredata;
  1064.             case GOTCRCG:
  1065.                 n = 10;
  1066.                 putsec(secbuf, Rxcount);
  1067.                 rxbytes += Rxcount;
  1068.                 goto moredata;
  1069.             case GOTCRCE:
  1070.                 n = 10;
  1071.                 putsec(secbuf, Rxcount);
  1072.                 rxbytes += Rxcount;
  1073.                 goto nxthdr;
  1074.             }
  1075.         }
  1076.     }
  1077. }
  1078.  
  1079. /*
  1080.  * Send a string to the modem, processing for \336 (sleep 1 sec)
  1081.  *   and \335 (break signal)
  1082.  */
  1083. zmputs(s)
  1084. char *s;
  1085. {
  1086.     register c;
  1087.  
  1088.     while (*s) {
  1089.         switch (c = *s++) {
  1090.         case '\336':
  1091.             sleep(1); continue;
  1092.         case '\335':
  1093.             sendbrk(); continue;
  1094.         default:
  1095.             sendline(c);
  1096.         }
  1097.     }
  1098. }
  1099.  
  1100. /*
  1101.  * Close the receive dataset, return OK or ERROR
  1102.  */
  1103. closeit()
  1104. {
  1105.     if (Topipe) {
  1106.         if (pclose(fout)) {
  1107.             return ERROR;
  1108.         }
  1109.         return OK;
  1110.     }
  1111.     if (fclose(fout)==ERROR) {
  1112.         fprintf(stderr, "file close ERROR\n");
  1113.         return ERROR;
  1114.     }
  1115.     if (Modtime) {
  1116.         timep[0] = time(NULL);
  1117.         timep[1] = Modtime;
  1118.         utime(Pathname, timep);
  1119.     }
  1120.     if (Filemode)
  1121.         chmod(Pathname, (07777 & Filemode));
  1122.     return OK;
  1123. }
  1124.  
  1125. /*
  1126.  * Ack a ZFIN packet, let byegones be byegones
  1127.  */
  1128. ackbibi()
  1129. {
  1130.     register n;
  1131.  
  1132.     vfile("ackbibi:");
  1133.     Readnum = 1;
  1134.     stohdr(0L);
  1135.     for (n=4; --n>=0; ) {
  1136.         zshhdr(ZFIN, Txhdr);
  1137.         for (;;) {
  1138.             switch (readline(100)) {
  1139.             case 'O':
  1140.                 readline(1);    /* Discard 2nd 'O' */
  1141.                 /* ***** FALL THRU TO ***** */
  1142.             case TIMEOUT:
  1143.                 vfile("ackbibi complete");
  1144.                 return;
  1145.             default:
  1146.                 break;
  1147.             }
  1148.         }
  1149.     }
  1150. }
  1151.  
  1152. /*
  1153.  * Local console output simulation
  1154.  */
  1155. bttyout(c)
  1156. {
  1157.     if (Verbose || fromcu())
  1158.         putc(c, stderr);
  1159. }
  1160.  
  1161. /*
  1162.  * Strip leading ! if present, do shell escape. 
  1163.  */
  1164. sys2(s)
  1165. register char *s;
  1166. {
  1167.     if (*s == '!')
  1168.         ++s;
  1169.     return system(s);
  1170. }
  1171. /*
  1172.  * Strip leading ! if present, do exec.
  1173.  */
  1174. exec2(s)
  1175. register char *s;
  1176. {
  1177.     if (*s == '!')
  1178.         ++s;
  1179.     mode(0);
  1180.     execl("/bin/sh", "sh", "-c", s);
  1181. }
  1182.  
  1183.